﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.ObjectModel;
using BingVis.SilverlightClient.Common;
using BingVis.SilverlightClient.GoogleTransit;
using System.Windows.Threading;
using System.Windows;
using System.Threading;
using System.Diagnostics;
using System.ComponentModel;


namespace BingVis.SilverlightClient
{
    
    public delegate void CompressedFareFileFinishedLoadingEvent(object sender, EventArgs args);

    public class FareListViewModel : ViewModelBase
    {
        public const int MAX_FARETAP_COUNT = 96 * 512; // 24 hours * 4 quarter hours * 512 stops.
        public DelegateCommand<IFareTap> AddFare { get; private set; }
        public DelegateCommand<string> ShowFaresForStop { get; private set; }
        public DelegateCommand<object> ClearFares { get; private set; }
        public DelegateCommand<IGTFSFile> LoadCompressedFareFile { get; private set; }

        public event CompressedFareFileFinishedLoadingEvent CompressedFareFileFinishedLoading;

        private CompressedFareTapLoader _compressedFareLoader;
        private IEnumerable<CompressedFareTap> _compressedFareTaps;

        private ObservableCollection<FareViewModel> fares;
        public ObservableCollection<FareViewModel> Fares
        {
            get { return fares; }
            set
            {
                if (fares != value)
                {
                    fares = value;
                    OnPropertyChanged("Fares");
                }
            }
        }
        
        private bool loadedCompressedFares;
        public bool LoadedCompressedFares
        {
            get { return loadedCompressedFares; }
            private set
            {
                if (loadedCompressedFares != value)
                {
                    loadedCompressedFares = value;
                    OnPropertyChanged("LoadedCompressedFares");
                }
            }
        }

        public decimal FaresTotal
        {
            get { return Fares.Sum(x => x.FareTotal); }            
        }
        public string FormattedFaresTotal { get { return FaresTotal.ToString("C"); } }


        public FareListViewModel()
        {
            Fares = FareViewUtility.EmptyQuarterHoursList;
            AddFare = new DelegateCommand<IFareTap>(fare => Deployment.Current.Dispatcher.BeginInvoke(new AddFareDelegate(OnAddFare), fare), false);
            ShowFaresForStop = new DelegateCommand<string>(OnShowFaresForStop);
            ClearFares = new DelegateCommand<object>(OnClearFares);
            LoadCompressedFareFile = new DelegateCommand<IGTFSFile>(OnLoadCompressedFareFile);
        }

        private delegate void AddFareDelegate(IFareTap fare);

        private void OnShowFaresForStop(string stopName)
        {
            ClearFares.Execute(null);

            if (!LoadedCompressedFares)
                return;

            // TODO: If people start clicking on a bunch of stops before the worker gets done there will be concurrency issues.
            BackgroundWorker fareWorker = new BackgroundWorker();
            fareWorker.WorkerReportsProgress = false;
            fareWorker.WorkerSupportsCancellation = false;
            fareWorker.DoWork += new DoWorkEventHandler(fareWorker_DoWork);
            fareWorker.RunWorkerAsync(stopName);            
        }

        void fareWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            var stopName = e.Argument as string;
            foreach (var fare in _compressedFareTaps                                            
                                            .Where(x => x != null 
                                                && FareStopLookup.CompareStopNames(x.StopName, StopNameLookup.GetStopName(stopName))))
            {
                AddFare.Execute(new FareTap()
                                    {
                                        Amount = fare.Amount,
                                        StopName = fare.StopName,
                                        Time = fare.Time
                                    }
                );
            }
        }

        private void OnAddFare(IFareTap fare)
        {
            // TODO: make the QuarterHour thing changeable, i.e. Hourly or HalfHour.
            var fView = Fares.FirstOrDefault(x => x.QuarterHour == fare.Time.GetQuarterHour());
            if (fView == null)
            {
                fView = new FareViewModel(fare.Time.GetQuarterHour());
                Fares.Add(fView);
            }

            fView.AddFareTotal(fare.Amount);
            OnPropertyChanged("Fares");
            OnPropertyChanged("FaresTotal");
            OnPropertyChanged("FormattedFaresTotal");
        }

        private void OnClearFares(object x)
        {
            this.Fares.Clear();

            // Set the beginning and ending of the day... for aesthetics...             
            
            this.AddFare.Execute(new FareTap() { Amount = 0.00M, Time = GetBeginningDate() });
            this.AddFare.Execute(new FareTap() { Amount = 0.00M, Time = GetEndDate() });            
        }

        private DateTime GetEndDate()
        {
            if ( !this.LoadedCompressedFares )
                return new DateTime(2009, 8, 12, 4, 0, 0);

            return GetBeginningDate()
                .AddDays(1)
                .WithTimeOfDay(2);
        }

        private DateTime GetBeginningDate()
        {
            if ( !this.LoadedCompressedFares)
                return new DateTime(2009, 8, 13, 2, 0, 0);

            return _compressedFareTaps.Where(x => x != null)
                .OrderBy(x => x.Time)
                .Distinct()
                .First()
                .Time
                .WithTimeOfDay(4);
        }

        private void OnLoadCompressedFareFile(IGTFSFile compressedFareFile)
        {
            if (_compressedFareLoader == null)
            {
                _compressedFareLoader = new CompressedFareTapLoader(compressedFareFile);                
            }

            _compressedFareLoader.DoneProcessing += new DoneProcessingEvent(OnCompressedFareDoneProcessing);

            LoadedCompressedFares = false;
            _compressedFareLoader.ProcessFile();
        }
        
        private void OnCompressedFareDoneProcessing(object sender, DoneProcessingEventArgs args)
        {   
            var tmpCopy = new CompressedFareTap[MAX_FARETAP_COUNT];
            _compressedFareLoader.Items.CopyTo(tmpCopy);
            _compressedFareTaps = tmpCopy;

            // Clean up the fare loader...
            _compressedFareLoader.DoneProcessing -= new DoneProcessingEvent(OnCompressedFareDoneProcessing);
            _compressedFareLoader.Items.Clear();
            _compressedFareLoader = null;

            LoadedCompressedFares = true;
            
            if (CompressedFareFileFinishedLoading != null)
                CompressedFareFileFinishedLoading(this, new EventArgs());
        }
    }
}
